<!doctype html>
<!-- 声明文档类型为 HTML5，让浏览器以 HTML5 标准解析该文档 -->
<html lang="en">
<!-- 定义 HTML 文档的根元素，设置语言为英语 -->
<head>
    <!-- 设置文档的字符编码为 UTF-8，确保能正确显示各种语言字符 -->
    <meta charset="UTF-8">
    <!-- 配置视口，使页面在不同设备上自适应显示 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 设置页面在浏览器标签页显示的标题 -->
    <title>方案4-判断初始化复杂json多层数据-使用js数据类型默认值</title>
</head>
<body>
<!-- HTML 注释，提示将 div 元素放在 body 内符合规范 -->
<!-- 将 div 元素移到 body 内部，符合 HTML 规范 -->
<!-- 创建一个 div 容器，用于包裹其他元素 -->
<div>
    <!-- 创建一个段落元素，id 为 test，初始内容后续会被替换 -->
    <p id="test">原始内容将被替换</p>
</div>
<script>
    /**
     * 判断初始化复杂json多层数据-使用js数据类型默认值
     * processJsonData 函数的主要目的是处理 JSON 数据，将其中的 null 值根据 typeConfig 中指定的默认值进行替换。
     * @param {string|object} jsonData - 需要处理的 JSON 数据，可以是 JSON 字符串或者 JavaScript 对象。
     * @param {object} typeConfig - 配置对象，用于指定 jsonData 中各个字段的默认值。
     * @returns {object|null} - 处理后的 JavaScript 对象，如果解析失败则返回 null。
     */
    function processJsonData(jsonData, typeConfig) {
        // 检查 typeConfig 是否为有效的对象，若不是则输出错误信息并返回 null
        if (typeof typeConfig !== 'object' || typeConfig === null) {
            console.error('typeConfig 必须是一个对象');
            return null;
        }

        let data;
        try {
            // 若 jsonData 是字符串，尝试将其解析为 JavaScript 对象；若本身是对象则直接使用
            data = typeof jsonData === 'string' ? JSON.parse(jsonData) : jsonData;
        } catch (error) {
            // 处理 JSON 解析失败的情况，输出错误信息并返回 null
            console.error('JSON 解析失败:', error);
            return null;
        }

        /**
         * 递归遍历对象，将 null 值替换为默认值
         * @param {object} obj - 需要处理的对象
         * @param {object} config - 当前层级对应的配置对象
         * @returns {object} - 处理后的对象
         */
        function traverseAndInit(obj, config) {
            // 使用 Object.keys 遍历对象的自有属性，避免遍历原型链上的属性
            Object.keys(obj).forEach(key => {
                // 获取当前键对应的值
                const value = obj[key];
                // 获取该键在当前配置对象中对应的默认值
                const defaultValue = config ? config[key] : undefined;
                // 如果值为 null 且默认值存在，则将值替换为默认值
                if (value === null && defaultValue !== undefined) {
                    obj[key] = defaultValue;
                }
                // 如果值是对象且不为 null，则递归调用 traverseAndInit 处理该对象
                else if (typeof value === 'object' && value !== null) {
                    // 获取该键在当前配置对象中对应的子配置对象
                    const subConfig = config ? config[key] : {};
                    traverseAndInit(value, subConfig);
                }
            });
            // 返回处理后的对象
            return obj;
        }

        // 调用 traverseAndInit 函数处理数据并返回结果
        return traverseAndInit(data, typeConfig);
    }

    // 定义一个示例 JSON 数据，这里是一个 JavaScript 对象，包含多个值为 null 的字段
    const jsonData = {
        "userList": null,
        "deptList": null,
        "userInfo": {"username": "abc", "likeArray": null},
        "userName": null,
        "userAge": null
    };

    // 定义一个配置对象，为 jsonData 中的每个字段指定默认值
    const typeConfig = {
        "userList": [], // userList 字段的默认值为一个空数组
        "deptList": [], // deptList 字段的默认值为一个空数组
        "userInfo": {
            "username": "",
            "likeArray": []
        }, // userInfo 字段的默认值为一个对象
        "userName": "", // userName 字段的默认值为一个空字符串
        "userAge": 0    // userAge 字段的默认值为数字 0
    };

    // 调用 processJsonData 函数处理 JSON 数据
    const processedData = processJsonData(jsonData, typeConfig);
    // 如果处理后的数据存在，则打印该数据
    if (processedData) {
        console.log(processedData);
        // 获取 id 为 test 的元素，将处理后的数据以 JSON 字符串形式写入其 innerHTML
        document.getElementById("test").innerHTML = JSON.stringify(processedData);
    }
</script>
</body>
</html>
